/**
 * This version is stamped on May 10, 2016
 *
 * Contact:
 *   Louis-Noel Pouchet <pouchet.ohio-state.edu>
 *   Tomofumi Yuki <tomofumi.yuki.fr>
 *
 * Web address: http://polybench.sourceforge.net
 */
/*
 * polybench.h: this file is part of PolyBench/C
 *
 * Polybench header for instrumentation.
 *
 * Programs must be compiled with `-I utilities utilities/polybench.c'
 *
 * Optionally, one can define:
 *
 * -DPOLYBENCH_TIME, to report the execution time,
 *   OR (exclusive):
 * -DPOLYBENCH_PAPI, to use PAPI H/W counters (defined in polybench.c)
 *
 *
 * See README or utilities/polybench.c for additional options.
 *
 */
#ifndef POLYBENCH_H
#define POLYBENCH_H

#include <stdlib.h>

/* Array padding. By default, none is used. */
#ifndef POLYBENCH_PADDING_FACTOR
/* default: */
#define POLYBENCH_PADDING_FACTOR 0
#endif

/* Inter-array padding, for use with . By default, none is used. */
#ifndef POLYBENCH_INTER_ARRAY_PADDING_FACTOR
/* default: */
#define POLYBENCH_INTER_ARRAY_PADDING_FACTOR 0
#undef POLYBENCH_ENABLE_INTARRAY_PAD
#else
#define POLYBENCH_ENABLE_INTARRAY_PAD
#endif

/* C99 arrays in function prototype. By default, do not use. */
#ifdef POLYBENCH_USE_C99_PROTO
#define POLYBENCH_C99_SELECT(x, y) y
#else
/* default: */
#define POLYBENCH_C99_SELECT(x, y) x
#endif

/* 修改default */
#define POLYBENCH_USE_SCALAR_LB

/* Scalar loop bounds in SCoPs. By default, use parametric loop bounds. */
#ifdef POLYBENCH_USE_SCALAR_LB
#define POLYBENCH_LOOP_BOUND(x, y) x
#else
/* default: */
#define POLYBENCH_LOOP_BOUND(x, y) y
#endif

/* Use the 'restrict' keyword to declare that the different arrays do not
 * alias. By default, we do not use it as it is only supported in C99 and
 * even here several compilers do not properly get it.
 */
#ifdef POLYBENCH_USE_RESTRICT
#define POLYBENCH_RESTRICT restrict
#else
/* default: */
#define POLYBENCH_RESTRICT
#endif

/* Macros to reference an array. Generic for heap and stack arrays
   (C99).  Each array dimensionality has his own macro, to be used at
   declaration or as a function argument.
   Example:
   int b[x] => POLYBENCH_1D_ARRAY(b, x)
   int A[N][N] => POLYBENCH_2D_ARRAY(A, N, N)
*/
#ifndef POLYBENCH_STACK_ARRAYS
#define POLYBENCH_ARRAY(x) *x
#ifdef POLYBENCH_ENABLE_INTARRAY_PAD
#define POLYBENCH_FREE_ARRAY(x) polybench_free_data((void *)x);
#else
#define POLYBENCH_FREE_ARRAY(x) free((void *)x);
#endif
#define POLYBENCH_DECL_VAR(x) (*x)
#else
#define POLYBENCH_ARRAY(x) x
#define POLYBENCH_FREE_ARRAY(x)
#define POLYBENCH_DECL_VAR(x) x
#endif
/* Macros for using arrays in the function prototypes. */
#define POLYBENCH_1D(var, dim1, ddim1)                         \
    var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1, ddim1) + \
        POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_2D(var, dim1, dim2, ddim1, ddim2)            \
    var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1, ddim1) + \
        POLYBENCH_PADDING_FACTOR]                              \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_3D(var, dim1, dim2, dim3, ddim1, ddim2, ddim3)      \
    var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1, ddim1) +        \
        POLYBENCH_PADDING_FACTOR]                                     \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR] \
       [POLYBENCH_C99_SELECT(dim3, ddim3) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_4D(var, dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, ddim4) \
    var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1, ddim1) +                \
        POLYBENCH_PADDING_FACTOR]                                             \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR]         \
       [POLYBENCH_C99_SELECT(dim3, ddim3) + POLYBENCH_PADDING_FACTOR]         \
       [POLYBENCH_C99_SELECT(dim4, ddim4) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_5D(var, dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, \
                     ddim4, ddim5)                                           \
    var[POLYBENCH_RESTRICT POLYBENCH_C99_SELECT(dim1, ddim1) +               \
        POLYBENCH_PADDING_FACTOR]                                            \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR]        \
       [POLYBENCH_C99_SELECT(dim3, ddim3) + POLYBENCH_PADDING_FACTOR]        \
       [POLYBENCH_C99_SELECT(dim4, ddim4) + POLYBENCH_PADDING_FACTOR]        \
       [POLYBENCH_C99_SELECT(dim5, ddim5) + POLYBENCH_PADDING_FACTOR]
/* Macros for using arrays within the functions. */
#define POLYBENCH_1D_F(var, dim1, ddim1) \
    var[POLYBENCH_C99_SELECT(dim1, ddim1) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_2D_F(var, dim1, dim2, ddim1, ddim2)                 \
    var[POLYBENCH_C99_SELECT(dim1, ddim1) + POLYBENCH_PADDING_FACTOR] \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_3D_F(var, dim1, dim2, dim3, ddim1, ddim2, ddim3)    \
    var[POLYBENCH_C99_SELECT(dim1, ddim1) + POLYBENCH_PADDING_FACTOR] \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR] \
       [POLYBENCH_C99_SELECT(dim3, ddim3) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_4D_F(var, dim1, dim2, dim3, dim4, ddim1, ddim2, ddim3, \
                       ddim4)                                            \
    var[POLYBENCH_C99_SELECT(dim1, ddim1) + POLYBENCH_PADDING_FACTOR]    \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR]    \
       [POLYBENCH_C99_SELECT(dim3, ddim3) + POLYBENCH_PADDING_FACTOR]    \
       [POLYBENCH_C99_SELECT(dim4, ddim4) + POLYBENCH_PADDING_FACTOR]
#define POLYBENCH_5D_F(var, dim1, dim2, dim3, dim4, dim5, ddim1, ddim2, ddim3, \
                       ddim4, ddim5)                                           \
    var[POLYBENCH_C99_SELECT(dim1, ddim1) + POLYBENCH_PADDING_FACTOR]          \
       [POLYBENCH_C99_SELECT(dim2, ddim2) + POLYBENCH_PADDING_FACTOR]          \
       [POLYBENCH_C99_SELECT(dim3, ddim3) + POLYBENCH_PADDING_FACTOR]          \
       [POLYBENCH_C99_SELECT(dim4, ddim4) + POLYBENCH_PADDING_FACTOR]          \
       [POLYBENCH_C99_SELECT(dim5, ddim5) + POLYBENCH_PADDING_FACTOR]

/* Macros to allocate heap arrays.
   Example:
   polybench_alloc_2d_array(N, M, double) => allocates N x M x sizeof(double)
                                          and returns a pointer to the 2d array
 */
#define POLYBENCH_ALLOC_1D_ARRAY(n1, type)                             \
    (type(*)[(unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR]) \
        polybench_alloc_data((unsigned long long int)(n1) +            \
                                 POLYBENCH_PADDING_FACTOR,             \
                             sizeof(type))
#define POLYBENCH_ALLOC_2D_ARRAY(n1, n2, type)                             \
    (type(*)[(unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR]      \
            [(unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR])     \
        polybench_alloc_data(                                              \
            ((unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR) *    \
                ((unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR), \
            sizeof(type))
#define POLYBENCH_ALLOC_3D_ARRAY(n1, n2, n3, type)                          \
    (type(*)[(unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n3) + POLYBENCH_PADDING_FACTOR])      \
        polybench_alloc_data(                                               \
            ((unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR) *     \
                ((unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR) * \
                ((unsigned long long int)(n3) + POLYBENCH_PADDING_FACTOR),  \
            sizeof(type))
#define POLYBENCH_ALLOC_4D_ARRAY(n1, n2, n3, n4, type)                      \
    (type(*)[(unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n3) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n4) + POLYBENCH_PADDING_FACTOR])      \
        polybench_alloc_data(                                               \
            ((unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR) *     \
                ((unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR) * \
                ((unsigned long long int)(n3) + POLYBENCH_PADDING_FACTOR) * \
                ((unsigned long long int)(n4) + POLYBENCH_PADDING_FACTOR),  \
            sizeof(type))
#define POLYBENCH_ALLOC_5D_ARRAY(n1, n2, n3, n4, n5, type)                  \
    (type(*)[(unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n3) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n4) + POLYBENCH_PADDING_FACTOR]       \
            [(unsigned long long int)(n5) + POLYBENCH_PADDING_FACTOR])      \
        polybench_alloc_data(                                               \
            ((unsigned long long int)(n1) + POLYBENCH_PADDING_FACTOR) *     \
                ((unsigned long long int)(n2) + POLYBENCH_PADDING_FACTOR) * \
                ((unsigned long long int)(n3) + POLYBENCH_PADDING_FACTOR) * \
                ((unsigned long long int)(n4) + POLYBENCH_PADDING_FACTOR) * \
                ((unsigned long long int)(n5) + POLYBENCH_PADDING_FACTOR),  \
            sizeof(type))

/* Macros for array declaration. */
#ifndef POLYBENCH_STACK_ARRAYS
#define POLYBENCH_1D_ARRAY_DECL(var, type, dim1, ddim1)        \
    type POLYBENCH_1D_F(POLYBENCH_DECL_VAR(var), dim1, ddim1); \
    var = POLYBENCH_ALLOC_1D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1), type);
#define POLYBENCH_2D_ARRAY_DECL(var, type, dim1, dim2, ddim1, ddim2)        \
    type POLYBENCH_2D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, ddim1, ddim2); \
    var = POLYBENCH_ALLOC_2D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1),       \
                                   POLYBENCH_C99_SELECT(dim2, ddim2), type);
#define POLYBENCH_3D_ARRAY_DECL(var, type, dim1, dim2, dim3, ddim1, ddim2,       \
                                ddim3)                                           \
    type POLYBENCH_3D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, ddim1, ddim2, \
                        ddim3);                                                  \
    var = POLYBENCH_ALLOC_3D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1),            \
                                   POLYBENCH_C99_SELECT(dim2, ddim2),            \
                                   POLYBENCH_C99_SELECT(dim3, ddim3), type);
#define POLYBENCH_4D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, ddim1,       \
                                ddim2, ddim3, ddim4)                            \
    type POLYBENCH_4D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, ddim1, \
                        ddim2, ddim3, ddim4);                                   \
    var = POLYBENCH_ALLOC_4D_ARRAY(POLYBENCH_C99_SELECT(dim1, ddim1),           \
                                   POLYBENCH_C99_SELECT(dim2, ddim2),           \
                                   POLYBENCH_C99_SELECT(dim3, ddim3),           \
                                   POLYBENCH_C99_SELECT(dim4, ddim4), type);
#define POLYBENCH_5D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, dim5,       \
                                ddim1, ddim2, ddim3, ddim4, ddim5)             \
    type POLYBENCH_5D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, dim5, \
                        ddim1, ddim2, ddim3, ddim4, ddim5);                    \
    var = POLYBENCH_ALLOC_5D_ARRAY(                                            \
        POLYBENCH_C99_SELECT(dim1, ddim1), POLYBENCH_C99_SELECT(dim2, ddim2),  \
        POLYBENCH_C99_SELECT(dim3, ddim3), POLYBENCH_C99_SELECT(dim4, ddim4),  \
        POLYBENCH_C99_SELECT(dim5, ddim5), type);
#else
#define POLYBENCH_1D_ARRAY_DECL(var, type, dim1, ddim1) \
    type POLYBENCH_1D_F(POLYBENCH_DECL_VAR(var), dim1, ddim1);
#define POLYBENCH_2D_ARRAY_DECL(var, type, dim1, dim2, ddim1, ddim2) \
    type POLYBENCH_2D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, ddim1, ddim2);
#define POLYBENCH_3D_ARRAY_DECL(var, type, dim1, dim2, dim3, ddim1, ddim2,       \
                                ddim3)                                           \
    type POLYBENCH_3D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, ddim1, ddim2, \
                        ddim3);
#define POLYBENCH_4D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, ddim1,       \
                                ddim2, ddim3, ddim4)                            \
    type POLYBENCH_4D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, ddim1, \
                        ddim2, ddim3, ddim4);
#define POLYBENCH_5D_ARRAY_DECL(var, type, dim1, dim2, dim3, dim4, dim5,       \
                                ddim1, ddim2, ddim3, ddim4, ddim5)             \
    type POLYBENCH_5D_F(POLYBENCH_DECL_VAR(var), dim1, dim2, dim3, dim4, dim5, \
                        ddim1, ddim2, ddim3, ddim4, ddim5);
#endif

/* Dead-code elimination macros. Use argc/argv for the run-time check. */
#ifndef POLYBENCH_DUMP_ARRAYS
#define POLYBENCH_DCE_ONLY_CODE if (argc > 42 && !strcmp(argv[0], ""))
#else
#define POLYBENCH_DCE_ONLY_CODE
#endif

#define POLYBENCH_DUMP_TARGET stderr
#define POLYBENCH_DUMP_START \
    fprintf(POLYBENCH_DUMP_TARGET, "==BEGIN DUMP_ARRAYS==\n")
#define POLYBENCH_DUMP_FINISH \
    fprintf(POLYBENCH_DUMP_TARGET, "==END   DUMP_ARRAYS==\n")
#define POLYBENCH_DUMP_BEGIN(s) \
    fprintf(POLYBENCH_DUMP_TARGET, "begin dump: %s", s)
#define POLYBENCH_DUMP_END(s) \
    fprintf(POLYBENCH_DUMP_TARGET, "\nend   dump: %s\n", s)

#define polybench_prevent_dce(func) \
    POLYBENCH_DCE_ONLY_CODE         \
    func

/* Performance-related instrumentation. See polybench.c */
#define polybench_start_instruments
#define polybench_stop_instruments
#define polybench_print_instruments

/* PAPI support. */
#ifdef POLYBENCH_PAPI
extern const unsigned int polybench_papi_eventlist[];
#undef polybench_start_instruments
#undef polybench_stop_instruments
#undef polybench_print_instruments
#define polybench_set_papi_thread_report(x) \
    polybench_papi_counters_threadid = x;
#define polybench_start_instruments                               \
    polybench_prepare_instruments();                              \
    polybench_papi_init();                                        \
    int evid;                                                     \
    for (evid = 0; polybench_papi_eventlist[evid] != 0; evid++) { \
        if (polybench_papi_start_counter(evid))                   \
            continue;

#define polybench_stop_instruments     \
    polybench_papi_stop_counter(evid); \
    }                                  \
    polybench_papi_close();

#define polybench_print_instruments polybench_papi_print();
#endif

/* Timing support. */
#if defined(POLYBENCH_TIME) || defined(POLYBENCH_GFLOPS)
#undef polybench_start_instruments
#undef polybench_stop_instruments
#undef polybench_print_instruments
#define polybench_start_instruments polybench_timer_start();
#define polybench_stop_instruments polybench_timer_stop();
#define polybench_print_instruments polybench_timer_print();
extern double polybench_program_total_flops;
extern void polybench_timer_start();
extern void polybench_timer_stop();
extern void polybench_timer_print();
#endif

/* PAPI support. */
#ifdef POLYBENCH_PAPI
extern int polybench_papi_start_counter(int evid);
extern void polybench_papi_stop_counter(int evid);
extern void polybench_papi_init();
extern void polybench_papi_close();
extern void polybench_papi_print();
#endif

/* Function prototypes. */
extern void *polybench_alloc_data(unsigned long long int n, int elt_size);
extern void polybench_free_data(void *ptr);

/* PolyBench internal functions that should not be directly called by */
/* the user, unless when designing customized execution profiling */
/* approaches. */
extern void polybench_flush_cache();
extern void polybench_prepare_instruments();

#endif /* !POLYBENCH_H */
